﻿using Mapster;
using RestSharp;
using Shop.Core.Enums;
using Shop.Core.ERP;
using Shop.Core.Repository;
using Shop.Core.Share;
using Shop.IServices;
using Shop.Model.Dto.Goo;
using Shop.Model.Dto.Orders;
using Shop.Model.Entity.Goo;
using Shop.Model.Entity.Order;
using Shop.Model.Entity.Use;
using Shop.Model.View.Order;
using SqlSugar;
using System.Net;
using System.Text;
using System.Text.Json;

namespace Shop.Services
{
    /// <summary>
    /// 
    /// </summary>
    public class OrderAppService : AbstractService, IOrdersAppService
    {
        private readonly RepositoryBase<Orders> _orderRepository;

        private readonly RepositoryBase<GoodsGoods> _goodsRepository;

        private readonly RepositoryBase<Delivery> _deliveryRepository;

        private readonly RepositoryBase<UseUser> _userRepository;

        private readonly IPaymentAppService _paymentAppService;

        private readonly IUserContext _userContext;

        private readonly IGoodsAppService _goodsAppService;

        public OrderAppService(RepositoryBase<Orders> orderRepository
            , RepositoryBase<UseUser> userRepository
            , RepositoryBase<GoodsGoods> goodsRepository
            , RepositoryBase<Delivery> deliveryRepository
            , IPaymentAppService paymentAppService
            , IUserContext userContext
            , IGoodsAppService goodsAppService)
        {
            _orderRepository = orderRepository;
            _userRepository = userRepository;
            _goodsRepository = goodsRepository;
            _deliveryRepository = deliveryRepository;
            _paymentAppService = paymentAppService;
            _userContext = userContext;
            _goodsAppService = goodsAppService;
        }


        /// <summary>
        /// 添加评论
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task AddComment(OrderEvaluateDto input)
        {
            var order = await _orderRepository.Includes(x => x.Goods)
                .Where(x => x.Id == input.OrderId && x.UserId == _userContext.Id)
                .FirstAsync();
            if (order == null)
                throw new Exception("订单不存在");
            else if (order.OrderStatus != (int)OrderStatus.Receipt)
                throw new Exception("订单状态存在问题，不能评价");

            input.items.ForEach(x =>
            {
                if (!order.Goods.Exists(y => y.Id == x.OrderItemId))
                    throw new Exception("参数错误");
            });

            List<GoodsReview> gr = new();
            foreach (var item in input.items)
            {
                var goodsInfo = order.Goods.First(x => x.Id == item.OrderItemId);
                gr.Add(new GoodsReview
                {
                    UserId = _userContext.Id,
                    OrderId = order.Id,
                    Score = item.Score,
                    Content = item.Content,
                    Video = item.Video,
                    Image = string.Join(",", item.Images),
                    GoodsSpecId = goodsInfo.GoodsSpecId,
                    GoodsId = goodsInfo.GoodsId,
                });
            }

            var gid = gr.Select(x => x.GoodsId).Distinct().ToList();
            await _orderRepository.Change<GoodsReview>().InsertRangeAsync(gr);
            await _goodsRepository.UpdateAsync(x => new GoodsGoods { CommentsCount = x.CommentsCount + 1 }, y => gid.Contains(y.Id));
            await _orderRepository.UpdateAsync(x => new Orders { OrderStatus = (int)OrderStatus.Evaluated }, y => y.Id == order.Id);
        }

        /// <summary>
        /// 取消订单
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task CancelOrder(long orderId)
        {
            var order = await _orderRepository.GetFirstAsync(x => x.Id == orderId && x.UserId == _userContext.Id);
            if (order != null && order.OrderStatus == (int)OrderStatus.Normal)
                await _orderRepository.UpdateAsync(x => new Orders { OrderStatus = (int)OrderStatus.Cancel, CloseTime = DateTime.Now }, y => y.Id == order.Id);
            else
                throw new Exception("订单取消失败");
        }

        /// <summary>
        /// 下单
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppResult<long>> Create(OrderAddDto input)
        {
            var user = await _userRepository.GetFirstAsync(x => x.Id == _userContext.Id);
            if (string.IsNullOrEmpty(user.Tele))
                return Problem("请先注册!");
            if (user.State == (int)UserState.CANCEL || user.State == (int)UserState.ACCOUNTFROZEN)
                return Problem("你的账号异常不能下单，如果有疑问请联系客服");

            var userAddr = await _userRepository.Queryable<UserAddress>().Where(x => x.Id == input.AddressId && x.UserId == _userContext.Id).FirstAsync();
            if (userAddr == null)
                return Problem("收货地址错误");

            var goodsIds = input.Goods.Select(x => x.GoodsId).Distinct().ToList();
            var goodsInfor = _goodsRepository.Includes(x => x.Size, y => y.Price)
                .Where(x => goodsIds.Contains(x.Id))
                .ToList();
            var invalid = goodsInfor.Where(x => x.Valid == false || x.Saleable == false || (x.UpTime.HasValue && x.UpTime > DateTime.Now) || (x.DownTime.HasValue && x.DownTime < DateTime.Now)).ToList();
            if (invalid.Any())
            {
                var gn = string.Join(",", invalid.Select(x => x.Name).ToList());
                return Problem($"商品({gn})不能购买");
            }

            List<OrdersGoods> orderGoods = new();
            int Point = 0;
            foreach (var item in input.Goods)
            {
                var goods = goodsInfor.First(x => x.Id == item.GoodsId);
                var size = goods.Size.First(x => x.Id == item.GoodsSpecId);
                orderGoods.Add(new OrdersGoods
                {
                    GoodsId = goods.Id,
                    GoodsCode = size.Code,
                    GoodsName = goods.Name,
                    GoodsSpecId = size.Id,
                    GoodsSpecName = size.Name,
                    GoodsImage = size.Thumbnail,
                    GoodsQuantity = item.GoodsNum,
                    IsGift = false,
                    GoodsPrice = user.Grade == 1 ? size.Price.OnePrice : user.Grade == 2 ? size.Price.TwoPrice : user.Grade == 3 ? size.Price.ThreePrice : size.Price.Price,
                });
                Point += size.Point * item.GoodsNum;
            }
            orderGoods.ForEach(x => x.TotalPrice = x.GoodsPrice * x.GoodsQuantity);
            decimal total = orderGoods.Sum(x => x.GoodsPrice * x.GoodsQuantity);

            //todo 邮费计算
            var dto = new CalculateDeliveryCostDto()
            {
                ProvinceCode = userAddr.ProvinceCode,
                CityCode = userAddr.CityCode,
                DistrictCode = userAddr.DistrictCode,
                Goods = orderGoods.Select(x => new DeliveryGoodsDto { GoodsId = x.GoodsId, Count = x.GoodsQuantity, Total = x.TotalPrice }).ToList(),
            };
            var lfee = await _goodsAppService.CalculateDeliveryCost(dto);
            if (lfee.Code != 200)
                return Problem(lfee.Errors[0]);
            decimal logisticsFee = lfee.Data.SumFreight;
            //todo 优惠计算
            decimal discount = 0;
            //实际支付
            var payment = total + logisticsFee - discount;
            //赠品
            var SpecIds = input.Goods.Select(x => x.GoodsSpecId).Distinct().ToList();
            var goodsGift = await _goodsRepository.Queryable<GoodsGift>()
                .Where(x => SpecIds.Contains(x.GoodsSpecId) && x.IsDel == false && (!x.IsLimited || (x.IsLimited && x.GiftTotal > 0)))
                .Where(x => (x.UpTime == null || x.UpTime <= DateTime.Now) &&
                            (x.DownTime == null || x.DownTime > DateTime.Now)).ToListAsync();
            var changgift = new List<GoodsGift>();
            List<OrderGiftDto> giftList = new();
            if (goodsGift.Count > 0)
            {
                foreach (var item in goodsGift)
                {
                    var num = 0;
                    var g = input.Goods.First(x => x.GoodsSpecId == item.GoodsSpecId);
                    //运算赠品数量
                    if (item.IsSuperposition)
                        num = g.GoodsNum / item.BuyCount * item.GiveCount;
                    else
                        num = g.GoodsNum >= item.BuyCount ? item.GiveCount : 0;
                    //检查赠品剩余数量是否足够发送
                    if (item.IsLimited)
                        num = num > item.RemainderCount ? item.RemainderCount : num;
                    if (num > 0)
                    {
                        var gift = giftList.FirstOrDefault(x => x.GiftSpecId == item.GoodsSpecId);
                        if (gift == null)
                            giftList.Add(new OrderGiftDto { GiftId = item.GiftId, GiftSpecId = item.GiftSpecId, Num = num });
                        else
                            gift.Num += num;
                        if (item.IsLimited)
                        {
                            item.RemainderCount -= num;
                            changgift.Add(item);
                        }
                    }
                }
            }

            if (giftList.Count > 0)
            {
                var giftIds = giftList.Select(x => x.GiftId).Distinct().ToList();
                var giftInfor = await _goodsRepository.Includes(x => x.Size).Where(x => giftIds.Contains(x.Id)).ToListAsync();
                foreach (var item in giftList)
                {
                    var goods = giftInfor.First(x => x.Id == item.GiftId);
                    var size = goods.Size.First(x => x.Id == item.GiftSpecId);
                    orderGoods.Add(new OrdersGoods
                    {
                        GoodsId = goods.Id,
                        GoodsCode = size.Code,
                        GoodsName = goods.Name,
                        GoodsSpecId = size.Id,
                        GoodsSpecName = size.Name,
                        GoodsImage = size.Thumbnail,
                        GoodsQuantity = item.Num,
                        IsGift = true
                    });
                }
            }

            try
            {
                _orderRepository.BeginTran();
                var order = new Orders()
                {
                    Code = CreateOrderCode(),
                    UserId = user.Id,
                    ConsigneeName = userAddr.Name,
                    ConsigneePhone = userAddr.Telephone,
                    ConsigneeProvince = userAddr.Province,
                    ConsigneeCity = userAddr.City,
                    ConsigneeDistrict = userAddr.District,
                    ConsigneeTown = userAddr.Street,
                    ConsigneeAddress = userAddr.Detail,
                    GoodsAmount = total,
                    DiscountAmount = discount,
                    LogisticsFee = logisticsFee,
                    OrderAmount = payment,
                    Note = input.Note,
                    OrderStatus = (int)OrderStatus.Normal,
                    OrderType = (int)OrderType.NORMAL,
                    Point = Point
                };
                var orderId = await _orderRepository.InsertReturnBigIdentityAsync(order);

                orderGoods.ForEach(x => x.OrdersId = orderId);
                await _orderRepository.Change<OrdersGoods>().InsertRangeAsync(orderGoods);
                _orderRepository.CommitTran();

                var cartIds = input.Goods.Where(x => x.CartId.HasValue).Select(x => x.CartId).Distinct().ToList();
                if (cartIds.Any())
                    await _orderRepository.Change<GoodsCart>().DeleteAsync(x => cartIds.Contains(x.Id) && x.User == order.UserId);
                if (changgift.Any())
                    await _orderRepository.Change<GoodsGift>().UpdateRangeAsync(changgift);
                return orderId;
            }
            catch (Exception)
            {
                _orderRepository.RollbackTran();
                throw new Exception("下单失败");
            }
        }

        public async Task<AppResult<long>> CreateGiftBagOrder(GiftBagOrderAddDto input)
        {
            var user = await _userRepository.GetFirstAsync(x => x.Id == _userContext.Id);
            if (string.IsNullOrEmpty(user.Tele))
                return Problem("请先注册!");
            if (user.State == (int)UserState.CANCEL || user.State == (int)UserState.ACCOUNTFROZEN)
                return Problem("你的账号异常不能下单，如果有疑问请联系客服");

            var userAddr = await _userRepository.Queryable<UserAddress>().Where(x => x.Id == input.AddressId && x.UserId == _userContext.Id).FirstAsync();
            if (userAddr == null)
                return Problem("收货地址错误");

            var goods = await _goodsRepository.Includes(x => x.Size, y => y.Price).FirstAsync(x => input.GoodsId == x.Id);
            if(goods.Valid == false || (goods.UpTime.HasValue && goods.UpTime > DateTime.Now) || (goods.DownTime.HasValue && goods.DownTime < DateTime.Now) || goods.Type != 2)
                return Problem($"商品({goods.Name})不能购买");
            var level = await _goodsRepository.Queryable<GoodsUserLevel>().FirstAsync(x => x.GoodsId == input.GoodsId);
            if (level.UserGrade != user.Grade)
                return Problem($"用户等级不适合购买该礼包");

            var Isexit = await _orderRepository.AnyAsync(x => x.UserId == _userContext.Id && x.OrderType == (int)OrderType.UPGRADE && x.OrderStatus == (int)OrderStatus.Normal);
            if(Isexit)
                return Problem($"你有一个未支付的礼包订单");
            var size = goods.Size.First(x => x.Id == input.GoodsSpecId);
            try
            {
                _orderRepository.BeginTran();
                OrdersGoods ordergoods = new()
                {
                    GoodsId = goods.Id,
                    GoodsCode = size.Code,
                    GoodsName = goods.Name,
                    GoodsSpecId = size.Id,
                    GoodsSpecName = size.Name,
                    GoodsImage = size.Thumbnail,
                    GoodsQuantity = 1,
                    IsGift = false,
                    GoodsPrice = user.Grade == 1 ? size.Price.OnePrice : user.Grade == 2 ? size.Price.TwoPrice : user.Grade == 3 ? size.Price.ThreePrice : size.Price.Price,
                };
                ordergoods.TotalPrice = ordergoods.GoodsPrice;

                var order = new Orders()
                {
                    Code = CreateOrderCode(),
                    UserId = user.Id,
                    ConsigneeName = userAddr.Name,
                    ConsigneePhone = userAddr.Telephone,
                    ConsigneeProvince = userAddr.Province,
                    ConsigneeCity = userAddr.City,
                    ConsigneeDistrict = userAddr.District,
                    ConsigneeTown = userAddr.Street,
                    ConsigneeAddress = userAddr.Detail,
                    GoodsAmount = ordergoods.TotalPrice,
                    OrderAmount = ordergoods.TotalPrice,
                    OrderStatus = (int)OrderStatus.Normal,
                    OrderType = (int)OrderType.UPGRADE,
                    Point = size.Point
                };
                var orderId = await _orderRepository.InsertReturnBigIdentityAsync(order);

                ordergoods.OrdersId = orderId;
                await _orderRepository.Change<OrdersGoods>().InsertAsync(ordergoods);
                _orderRepository.CommitTran();
                return orderId;
            }
            catch (Exception)
            {
                _orderRepository.RollbackTran();
                return Problem("下单失败");
            }
        }

        /// <summary>
        /// 发货
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task Delivery(DeliveryNoticeData input)
        {
            var order = await _orderRepository.Includes(x => x.Goods)
                .Where(x => x.Code == input.so_id)
                .FirstAsync();
            if (order != null)
            {
                var delivery = await _deliveryRepository.GetFirstAsync(x => x.LogisticsNo == input.l_id);
                if (delivery == null)
                {
                    delivery = new Delivery
                    {
                        LogisticsCompany = input.logistics_company,
                        LogisticsCompanyCode = input.lc_id,
                        LogisticsNo = input.l_id,
                        SendTime = input.send_date
                    };
                    delivery.Id = await _deliveryRepository.InsertReturnBigIdentityAsync(delivery);
                }
                List<DeliveryItem> items = new();
                foreach (var item in input.items)
                {
                    _ = long.TryParse(item.outer_oi_id, out long orderGoodsId);
                    if (order.Goods.Exists(x => x.Id == orderGoodsId))
                    {
                        items.Add(new DeliveryItem
                        {
                            DeliveryId = delivery.Id,
                            OrderGoodsId = orderGoodsId,
                            OrderId = order.Id,
                            Quantity = item.qty
                        });
                    }
                }

                if (items.Count > 0)
                    await _deliveryRepository.Change<DeliveryItem>().InsertRangeAsync(items);

                if (order.OrderStatus == (int)OrderStatus.Paid)
                {
                    order.OrderStatus = (int)OrderStatus.Ship;
                    order.DeliveryTime = input.send_date;
                    await _orderRepository.UpdateAsync(order);
                }
            }
        }

        /// <summary>
        /// 订单详情
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<OrderDetailVo> GetOrderDetail(long orderId)
        {
            var order = await _orderRepository.Includes(x => x.Goods)
                .Where(x => x.Id == orderId && x.UserId == _userContext.Id).FirstAsync();
            if (order == null)
                throw new Exception("订单不存在");
            var result = order.Adapt<OrderDetailVo>();
            List<OrderGoodsDetailVo> goods = new();
            order.Goods.ForEach(x => goods.Add(x.Adapt<OrderGoodsDetailVo>()));
            var ids = _orderRepository.Queryable<DeliveryItem>().Where(x => x.OrderId == order.Id)
                .Select(x => x.DeliveryId)
                .Distinct()
                .ToList();
            var delivery = _orderRepository.Queryable<Delivery>().Where(x => ids.Contains(x.Id))
                .Select(x => new DeliveryVo
                {
                    LogisticsCompany = x.LogisticsCompany,
                    LogisticsNo = x.LogisticsNo,
                    SendTime = x.SendTime,
                }).ToList();
            result.OrderItem = goods;
            result.Deliveries = delivery;
            return result;
        }

        /// <summary>
        /// 订单列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public PageModel<OrderListVo> GetOrderList(PageSearchModel<int> input)
        {
            int totalNumber = 0;
            var query = _orderRepository.Includes(x => x.Goods)
                .WhereIF(input.Value > 0, x => x.OrderStatus == input.Value)
                .Where(x => x.UserId == _userContext.Id)
                .OrderByDescending(x => x.CreateTime)
                .ToPageList(input.PageIndex, input.PageSize, ref totalNumber);

            List<OrderListVo> data = new();
            if (query.Count > 0)
                query.ForEach(x => data.Add(x.Adapt<OrderListVo>()));

            return new PageModel<OrderListVo>(input, data, totalNumber);
        }

        /// <summary>
        /// 获取用户各个状态的订单数量
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<Dictionary<int, int>> GetOrderNum()
        {
            var statusList = new List<int>() { (int)OrderStatus.Normal, (int)OrderStatus.Paid, (int)OrderStatus.Ship, (int)OrderStatus.Receipt };
            Dictionary<int, int> resualt = new();
            foreach (var item in statusList)
                resualt.Add(item, 0);
            var data = await _orderRepository.Where(x => x.UserId == _userContext.Id && statusList.Contains(x.OrderStatus))
                .GroupBy(x => new { x.OrderStatus })
                .Select(x => new { id = x.OrderStatus, value = SqlFunc.RowCount() })
                .ToListAsync();
            data.ForEach(x => resualt[x.id] = x.value);
            return resualt;
        }

        /// <summary>
        /// 收货
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task OrderConfirm(long input)
        {
            var order = await _orderRepository.Where(x => x.Id == input && x.UserId == _userContext.Id && x.OrderStatus == (int)OrderStatus.Ship).FirstAsync();
            if (order == null)
                throw new Exception("确认收货失败");
            else
            {
                order.OrderStatus = (int)OrderStatus.Receipt;
                order.ReceiveTime = DateTime.Now;
                await _orderRepository.UpdateAsync(order);

                await _paymentAppService.Settlement(order.Id);
            }
        }

        /// <summary>
        /// 物流追踪
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AppResult<List<LogisticsTrackingVo>>> LogisticsTracking(long input)
        {
            List<LogisticsTrackingVo> result = new();
            var LogisticsNos = await _deliveryRepository.Queryable<Delivery,DeliveryItem>((t1, t2) => new object[]
            {
                JoinType.Left, t1.Id == t2.DeliveryId
            }).Where((t1, t2) => t2.OrderId == input)
            .Select((t1,t2)=> t1.LogisticsNo)
            .Distinct()
            .ToListAsync();
            if (LogisticsNos.Any())
            {
                foreach (var item in LogisticsNos)
                {
                    String url = "https://wuliu.market.alicloudapi.com" + "/kdi?no=" + item;
                    string appCode = "ce088ce36a394697861ee7904233c8e2";
                    var client = new RestClient(url);
                    var request = new RestRequest();
                    request.AddHeader("Authorization", "APPCODE " + appCode);
                    RestResponse response = client.Execute(request);
                    var data =  JsonSerializer.Deserialize<LogisticsTrackingResultVo>(response.Content, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
                    if (data.Msg.Equals("ok"))
                        result.Add(data.Result);
                }
            }

            return result;
        }

        /// <summary>
        /// 订单Code生成
        /// </summary>
        /// <returns></returns>
        private static string CreateOrderCode()
        {
            Random random = new Random();
            string strRandom = random.Next(10000, 99999).ToString();
            string code = $"DD{DateTime.Now.ToString("yyyyMMddHHmmssffff")}{strRandom}";
            return code;
        }
    }
}
